#include "node.h"
#include <iostream>

int Node::nbDeleteNode = 0;
int Node::nbNewNode = 0;

Node::Node()
{
        tl_x = 0;
        tl_y = 0;
        br_x = 0;
        br_y = 0;
        nodeLevel = 0;

        colorAverage = -1;
        colorAverageBlue = -1;
        colorAverageGreen = -1;
        colorAverageRed = -1;

        colored = false;
        nodeAncestor = NULL;

        nbNewNode++;
        //std::cout << "Nombre de Noeud Créer : " << nbNewNode << std::endl;
}

Node::Node(int x1, int y1, int x2, int y2, int level, Node* ancestor)
{
        tl_x = x1;
        tl_y = y1;
        br_x = x2;
        br_y = y2;
        nodeLevel = level;

        colorAverage = -1;
        colorAverageBlue = -1;
        colorAverageGreen = -1;
        colorAverageRed = -1;

        colored = false;
        nodeAncestor = ancestor;

        nbNewNode++;
        //std::cout << "Nombre de Noeud Créer : " << nbNewNode << std::endl;
}

Node::~Node()
{
        std::vector<Node*>::iterator it;
        for (int i = 0; i < 4; i++)
        {
                for (it = adjacentMatrix[i].begin(); it != adjacentMatrix[i].end(); )
                {
                        it = adjacentMatrix[i].erase(it);
                }
        }

        nbDeleteNode++;
        //std::cout << "Nombre de Noeud détruit : " << nbDeleteNode << std::endl;
}

void Node::RemoveAdjacentMatrix()
{
        std::vector<Node*>::iterator it;
        for (int i = 0; i < 4; i++)
        {
                for (it = adjacentMatrix[i].begin(); it != adjacentMatrix[i].end(); )
                {
                        it = adjacentMatrix[i].erase(it);
                }
        }
}

bool Node::IsHomogeneous(IplImage *img, int seuil, bool isColorImg)
{
        CvScalar pixel = cvGet2D(img, tl_y, tl_x);

        int blueMax = pixel.val[0] + seuil;
        int blueMin = pixel.val[0] - seuil;
        if (blueMax > 255)
                blueMax = 255;
        if (blueMin < 0)
                blueMin = 0;

        if (isColorImg)
        {
                int greenMax = pixel.val[1] + seuil;
                int greenMin = pixel.val[1] - seuil;
                if (greenMax > 255)
                        greenMax = 255;
                if (greenMin < 0)
                        greenMin = 0;

                int redMax = pixel.val[2] + seuil;
                int redMin = pixel.val[2] - seuil;
                if (redMax > 255)
                        redMax = 255;
                if (redMin < 0)
                        redMin = 0;

                for (int i = tl_x ; i <= br_x; i++)
                {
                        for (int j = tl_y ; j <= br_y ; j++)
                        {
                                if (i != tl_x && j != tl_y)
                                {
                                        pixel = cvGet2D(img, j, i);

                                        if (	blueMax < pixel.val[0] || blueMin > pixel.val[0] ||
                                                greenMax < pixel.val[1] || greenMin > pixel.val[1] ||
                                                redMax < pixel.val[2] || redMin > pixel.val[2])
                                                return false;
                                }
                        }
                }
        }
        else
        {
                for (int i = tl_x ; i <= br_x; i++)
                {
                        for (int j = tl_y ; j <= br_y ; j++)
                        {
                                if (i != tl_x && j != tl_y)
                                {
                                        pixel = cvGet2D(img, j, i);

                                        if (blueMax < pixel.val[0] || blueMin > pixel.val[0])
                                        {
                                                return false;
                                        }
                                }
                        }
                }
        }

        this->CalculateColorAverage(img, isColorImg);
        return true;
}

void Node::CalculateColorAverage(IplImage* img, bool isColorImg)
{
        int nbPixels;
        colorAverage = 0;

        if (isColorImg)
        {
                colorAverageBlue = 0;
                colorAverageGreen = 0;
                colorAverageRed = 0;
        }

        if (tl_x == br_x || tl_y == br_y)
        {
                if (tl_x != br_x)
                {
                        nbPixels = br_x - tl_x + 1;
                        for (int i = tl_x; i <= br_x; i++)
                        {
                                if (isColorImg)
                                {
                                        colorAverageBlue += cvGet2D(img, tl_y, i).val[0];
                                        colorAverageGreen += cvGet2D(img, tl_y, i).val[1];
                                        colorAverageRed += cvGet2D(img, tl_y, i).val[2];
                                }
                                else
                                        colorAverage += cvGet2D(img, tl_y, i).val[0];
                        }
                        if (isColorImg)
                        {
                                colorAverageBlue /= nbPixels;
                                colorAverageGreen /= nbPixels;
                                colorAverageRed /= nbPixels;
                                colorAverage = (colorAverageBlue + colorAverageGreen + colorAverageRed) / 3;
                        }
                        else
                                colorAverage /= nbPixels;
                }
                else if (tl_y != br_y)
                {
                        nbPixels = br_y - tl_y + 1;
                        for (int j = tl_y; j <= br_y; j++)
                        {
                                if (isColorImg)
                                {
                                        colorAverageBlue += cvGet2D(img, j, tl_x).val[0];
                                        colorAverageGreen += cvGet2D(img, j, tl_x).val[1];
                                        colorAverageRed += cvGet2D(img, j, tl_x).val[2];
                                }
                                else
                                        colorAverage += cvGet2D(img, j, tl_x).val[0];
                        }
                        if (isColorImg)
                        {
                                colorAverageBlue /= nbPixels;
                                colorAverageGreen /= nbPixels;
                                colorAverageRed /= nbPixels;
                                colorAverage = (colorAverageBlue + colorAverageGreen + colorAverageRed) / 3;
                        }
                        else
                                colorAverage /= nbPixels;
                }
                else
                {
                        if (isColorImg)
                        {
                                colorAverageBlue = cvGet2D(img, tl_y, tl_x).val[0];
                                colorAverageGreen = cvGet2D(img, tl_y, tl_x).val[1];
                                colorAverageRed = cvGet2D(img, tl_y, tl_x).val[2];
                                colorAverage = (colorAverageBlue + colorAverageGreen + colorAverageRed) / 3;
                        }
                        else
                                colorAverage = cvGet2D(img, tl_y, tl_x).val[0];
                }
        }
        else
        {
                nbPixels = (br_x - tl_x + 1) * (br_y - tl_y + 1);
                for (int i = tl_x; i <= br_x; i++)
                {
                        for (int j = tl_y; j <= br_y; j++)
                        {
                                if (isColorImg)
                                {
                                        colorAverageBlue += cvGet2D(img, j, i).val[0];
                                        colorAverageGreen += cvGet2D(img, j, i).val[1];
                                        colorAverageRed += cvGet2D(img, j, i).val[2];
                                }
                                else
                                        colorAverage += cvGet2D(img, j, i).val[0];
                        }
                }

                if (isColorImg)
                {
                        colorAverageBlue /= nbPixels;
                        colorAverageGreen /= nbPixels;
                        colorAverageRed /= nbPixels;
                        colorAverage = (colorAverageBlue + colorAverageGreen + colorAverageRed) / 3;
                }
                else
                        colorAverage /= nbPixels;
        }
}

void Node::ColorNode(IplImage* img, CvScalar pixel, int averageMax, int averageMin)
{
        for (int i = tl_x; i <= br_x; i++)
                for (int j = tl_y; j <= br_y; j++)
                        cvSet2D(img, j, i, pixel);

        colored = true;

        for (int k = 0 ; k < 4 ; k++)
        {
                if (!adjacentMatrix[k].empty())
                {
                        for (int l = 0 ; l < adjacentMatrix[k].size(); l++)
                        {
                                if (	!adjacentMatrix[k].at(l)->GetColored() &&
                                        adjacentMatrix[k].at(l)->GetColorAverage() <= averageMax &&
                                        adjacentMatrix[k].at(l)->GetColorAverage() >= averageMin)
                                {
                                        adjacentMatrix[k].at(l)->ColorNode(img, pixel, averageMax, averageMin);
                                }
                        }
                }
        }
}

/*void Node::ColorNode2(IplImage *img, CvScalar pixel, int averageMax, int averageMin) {

        //std::cout<< "ColorNode2 debut"<< std::endl;

        if (!colored) {

                colored = true;
                //Je colore la zone courrante
                for (int i = tl_x ; i <= br_x; i++)
                        for (int j = tl_y ; j <= br_y ; j++)
                                cvSet2D(img, j, i, pixel);



                for (int k = 0 ; k < 4 ; k++)
                {
                        if (!adjacentMatrix[k].empty())
                        {
                                //std::cout << "matrice non vide" << std::endl;
                                for (int l = 0 ; l < adjacentMatrix[k].size(); l++)
                                {
                                        //std::cout << "average du voisin " << averageMax << std::endl;
                                        //std::cout << "max " << averageMax << std::endl;
                                        if (	!adjacentMatrix[k].at(l)->GetColored() &&
                                                adjacentMatrix[k].at(l)->GetColorAverage() <= averageMax &&
                                                adjacentMatrix[k].at(l)->GetColorAverage() >= averageMin)
                                        {
                                                //std::cout << "on rentre dans le if pour colorier le voisin" << std::endl;

                                                /*for (int i = adjacentMatrix[k].at(l)->tl_x ; i < adjacentMatrix[k].at(l)->br_x; i++) {
                                                        for (int j = adjacentMatrix[k].at(l)->tl_y ; j < adjacentMatrix[k].at(l)->br_y ; j++) {
                                                                cvSet2D(img, j, i, pixel);
                                                        }
                                                }*/

                                                /*adjacentMatrix[k].at(l)->ColorNode2(img, pixel, averageMax, averageMin);
                                                //adjacentMatrix[k].at(l)->SetColored(true);

                                        }
                                }
                        }
                }

        }

        //std::cout<< "ColorNode2 fin "<< std::endl;
}*/

void Node::InheritAdjacentMatrixTop(std::vector<Node*> &top, bool side)
{
        // Note : Comme on découpe en largeur on le noeud père ne peut pas avoir plus de 2 voisins.
        // Donc un noeud fils ne peut hérité du père qu'un seul voisin.

        if (top.size() == 1)
        {
                adjacentMatrix[0].push_back(top.front());
                top.front()->AddNeighbourBottom(this);
        }
        else if (top.size() == 2)
        {
                if (side)
                {
                        adjacentMatrix[0].push_back(top.front());
                        top.front()->AddNeighbourBottom(this);
                }
                else
                {
                        adjacentMatrix[0].push_back(top.back());
                        top.back()->AddNeighbourBottom(this);
                }
        }
}

void Node::InheritAdjacentMatrixRight(std::vector<Node*> &right, bool side)
{
        // Puisqu'on découpe de gauche a droite et de haut en bas il y a forcément
        // qu'un seul voisin a droite et en bas.
        /*
        if (!right.empty())
        {
                adjacentMatrix[1].push_back(right.front());
                right.front()->AddNeighbourLeft(this);
        }
        /*else
                if (side)
                        adjacentMatrix[1].push_back(right.front());
                else
                        adjacentMatrix[1].push_back(right.back());*/

        if (right.size() == 1)
        {
                adjacentMatrix[1].push_back(right.front());
                right.front()->AddNeighbourLeft(this);
        }
        else if (right.size() == 2)
        {
                if (side)
                {
                        adjacentMatrix[1].push_back(right.front());
                        right.front()->AddNeighbourLeft(this);
                }
                else
                {
                        adjacentMatrix[1].push_back(right.back());
                        right.back()->AddNeighbourLeft(this);
                }
        }
}

void Node::InheritAdjacentMatrixBottom(std::vector<Node*> &bottom, bool side)
{
        if (!bottom.empty())
        {
                adjacentMatrix[2].push_back(bottom.front());
                bottom.front()->AddNeighbourTop(this);
        }
        /*else
                if (side)
                        adjacentMatrix[2].push_back(bottom.front());
                else
                        adjacentMatrix[2].push_back(bottom.back());*/
}

void Node::InheritAdjacentMatrixLeft(std::vector<Node*> &left, bool side)
{
        if (left.size() == 1)
        {
                adjacentMatrix[3].push_back(left.front());
                left.front()->AddNeighbourRight(this);
        }
        else if (left.size() == 2)
        {
                if (side)
                {
                        adjacentMatrix[3].push_back(left.front());
                        left.front()->AddNeighbourRight(this);
                }
                else
                {
                        adjacentMatrix[3].push_back(left.back());
                        left.back()->AddNeighbourRight(this);
                }
        }
}

void Node::AddNeighbourTop(Node *neighbour)
{
        std::vector<Node*>::iterator it = adjacentMatrix[0].begin();
        while (it != adjacentMatrix[0].end() && *it != neighbour->nodeAncestor)
                ++it;

        if (it != adjacentMatrix[0].end())
                adjacentMatrix[0].erase(it);

        adjacentMatrix[0].push_back(neighbour);
}

void Node::AddNeighbourRight(Node *neighbour)
{
        std::vector<Node*>::iterator it = adjacentMatrix[1].begin();
        while (it != adjacentMatrix[1].end() && *it != neighbour->nodeAncestor)
                ++it;

        if (it != adjacentMatrix[1].end())
                adjacentMatrix[1].erase(it);

        adjacentMatrix[1].push_back(neighbour);
}

void Node::AddNeighbourBottom(Node *neighbour)
{
        std::vector<Node*>::iterator it = adjacentMatrix[2].begin();
        while (it != adjacentMatrix[2].end() && *it != neighbour->nodeAncestor)
                ++it;

        if (it != adjacentMatrix[2].end())
                adjacentMatrix[2].erase(it);

        adjacentMatrix[2].push_back(neighbour);
}

void Node::AddNeighbourLeft(Node *neighbour)
{
        std::vector<Node*>::iterator it = adjacentMatrix[3].begin();
        while (it != adjacentMatrix[3].end() && *it != neighbour->nodeAncestor)
                ++it;

        if (it != adjacentMatrix[3].end())
                adjacentMatrix[3].erase(it);

        adjacentMatrix[3].push_back(neighbour);
}




